#include <iostream>
#include <string>
#include <vector>
#include <unistd.h>
#include <sys/wait.h>
#include <stdlib.h>
#include <time.h>
#include "Test.hpp"

class Channel
{
public:
    Channel(int wfd, pid_t subprocessid)
        : _wfd(wfd), _subprocessid(subprocessid)
    {
    }
    int GetWfd() { return _wfd; }
    pid_t GetSubProcessId() { return _wfd; }

    void WfdClose() { close(_wfd); }
    void wait()
    {
        pid_t id = waitpid(_subprocessid, nullptr, 0);
        if (id > 0)
        {
            std::cout << "wait " << _subprocessid << " process success" << std::endl;
        }
    }

private:
    int _wfd;
    pid_t _subprocessid;
};

// 子进程通过读取到管道中的内容来执行相应的任务
void work()
{
    int command = 0;
    while (true)
    {
        size_t n = read(0, &command, sizeof(command));
        if (n > 0)
        {
            if (command < 0 || command > 2)
                return;
            sleep(1);
            taskTable[command]();
        }
        else if (n == 0)
        {
            break;
        }
    }
}
// 发任务
void SendTask(std::vector<Channel> &channel)
{
    // 1.选择任务
    int num1 = rand() % 3;
    // 2.选择管道
    static int count = 0;
    count++;
    int num2 = count % channel.size();
    // 3.向管道中写任务
    int wfd = channel[num2].GetWfd();
    write(wfd, &num1, sizeof(num1));
}
// 创建管道、创建子进程
void CreatPipeAndSubpro(int num, std::vector<Channel> &channel, task_t task)
{
    for (int i = 0; i < num; ++i)
    {
        // 创建管道
        int pipefd[2] = {0};
        int n = pipe(pipefd);
        if (n < 0)
            exit(1);

        // 创建子进程
        pid_t id = fork();
        if (id == 0)
        {
            if (!channel.empty())
            {
                for (auto tmp : channel)
                {
                    tmp.WfdClose();
                }
            }
            // 子进程关闭pipefd[1]
            close(pipefd[1]);
            // 子进程执行工作
            dup2(pipefd[0], 0); // 将子进程rfd下标的内容拷贝到0下标下，这样本应从键盘中读取内容改成从管道读取内容
            task();
            // 执行完工作后关闭pipefd[0]
            close(pipefd[0]);

            std::cout << getppid() << " sub process quit..." << std::endl;
            exit(0);
        }

        // 父进程
        // 父进程关闭pipefd[0]
        close(pipefd[0]);
        channel.push_back(Channel(pipefd[1], id));
    }
}
void CleanChannel(std::vector<Channel> &channel)
{
    for (auto &tmp : channel)
    {
        tmp.WfdClose();
        tmp.wait();
    }
}
int main(int argc, char *argv[])
{
    if (argc != 2)
    {
        std::cerr << "Usage: " << argv[0] << "process num" << std::endl;
        exit(1);
    }

    // 1.创建任务表(函数指针数组)
    // 将任务表的下标写到管道中，子进程从管道中读取下标，子进程能够访问父进程中的数据，所以子进程能够拿到函数指针，从而执行任务
    InitTask();

    int num = std::stoi(argv[1]);
    std::vector<Channel> channel;
    // 2.创建管道、创建子进程
    // 最开始父进程没有向管道中写数据，所以num个子进程处于阻塞状态
    CreatPipeAndSubpro(num, channel, work);
    // 3.发任务
    for (int i = 0; i < 9; ++i)
    {
        SendTask(channel);
    }
    // 4.回收子进程
    // 关闭写段的fd，子进程在读的时候返回值为0，然后子进程退出，父进程等待
    CleanChannel(channel);

    return 0;
}
